﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Imple.Ex;

namespace Imple.Parse
{
    public class ParseTree
    {
        private ParseNode _nodeRoot;
        private ParseNode _currentNode;
        private delegate void OneParamHandler<T>(T t);
        private Dictionary<int, OneParamHandler<ParseNode>> _levelSpanMethodDic;

        public ParseTree()
        {
            _levelSpanMethodDic = new Dictionary<int, OneParamHandler<ParseNode>>();
            _levelSpanMethodDic.Add(0, AddSiblingNode);
            _levelSpanMethodDic.Add(1, AddNextNode);
            _levelSpanMethodDic.Add(-1, AddPreviousNode);
        }

        public void AddNode(ParseNode node)
        {
            if (null == node)
                throw new ArgumentNullException("node");
            if (null == _nodeRoot)
                InitTreeRoot();
            CheckExternalNodeStatus(node);

            int levelSpan = node.Level - _currentNode.Level;
            _levelSpanMethodDic[levelSpan](node);
            _currentNode = node;
        }

        private void AddPreviousNode(ParseNode node)
        {
            ParseNode aboveParent = FindAboveParent(_currentNode);
            aboveParent.Sibling = node;

            aboveParent.AboveParent.Children.Add(node);

            node.AboveParent = aboveParent.AboveParent;
            node.BelowParent = null;
            node.Children = new List<ParseNode>();
            node.Sibling = null;

            _currentNode.BelowParent = node;
        }

        private void AddNextNode(ParseNode node)
        {
            node.AboveParent = _currentNode;
            node.BelowParent = null;
            node.Children = new List<ParseNode>();
            node.Sibling = null;

            _currentNode.Children.Add(node);
            _currentNode.Sibling = null;
        }

        private void AddSiblingNode(ParseNode node)
        {
            ParseNode aboveParent = FindAboveParent(_currentNode);

            node.AboveParent = aboveParent;
            node.BelowParent = null;
            node.Children = new List<ParseNode>();
            node.Sibling = null;

            _currentNode.BelowParent = null;
            _currentNode.Children = new List<ParseNode>();
            _currentNode.Sibling = node;

            aboveParent.Children.Add(node);
        }

        private ParseNode FindAboveParent(ParseNode node)
        {
            return node.AboveParent;
        }

        private void CheckExternalNodeStatus(ParseNode node)
        {
            if (node.Level <= -1 || node.Level == Int32.MaxValue)
                throw new NodeLevelOutofRangeException(node.Level);

            int levelSpan = node.Level - _currentNode.Level;
            if (levelSpan > 1 || levelSpan < -1)
                throw new NodeLevelNotContinousException(_currentNode, node);
            if (null != node.AboveParent ||
                null != node.BelowParent ||
                null != node.Children ||
                null != node.Sibling)
                throw new InvalidOperationException("can not add a NOT NULL parent or child node.");
        }

        private void InitTreeRoot()
        {
            _nodeRoot = new ParseNode();

            _nodeRoot.Level = -1;
            _nodeRoot.AboveParent = null;
            _nodeRoot.BelowParent = null;
            _nodeRoot.Children = new List<ParseNode>();
            _nodeRoot.Sibling = null;
            _nodeRoot.Value = string.Empty;
            _nodeRoot.NodeType = XmlNodeType.None;
            _nodeRoot.LocalName = string.Empty;

            _currentNode = _nodeRoot;
        }

        public ParseNode Root
        {
            get { return _nodeRoot; }
        }
    }
}